import BleManager from 'react-native-ble-manager'
import {NativeEventEmitter, NativeModules, PermissionsAndroid, Platform} from 'react-native';
import {bytesToString,stringToBytes,UTF8} from 'convert-string'
import _ from 'lodash'
import {utf8to16,utf16to8} from './convertString';
import {isIos} from './native';

const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule)

const BLEWriteServiceUUID = '3a630002-6a86-46a2-a6e8-98b267e44877'
const BLENotifyServiceUUID = '3a630003-6a86-46a2-a6e8-98b267e44877'

let wake = (time) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // console.log(`${time }秒后醒来`)
            resolve({timeout:true})
        }, time * 1000)
    })
}


export default class BleUtil {


    static writeServiceUUID = null
    static writeCharacteristicUUID = null
    static readNotifyServiceUUID = null
    static readNotifyCharacteristicUUID = null
    //指令相关
    static currentCmd = null
    static cmdResults = []

    static init(){

        return BleManager.start({showAlert:false,restoreIdentifierKey:'satelite',
            forceLegacy:true
        })
    }

    static checkState(){
        return BleManager.checkState()
    }

    static addNewDeviceListener(cb){
        //搜索到一个新设备监听
        return bleManagerEmitter.addListener('BleManagerDiscoverPeripheral', (data) => {
            // console.log('BleManagerDiscoverPeripheral:', data);
            if(data.name && data.name.includes('TABE')){
                let id;  //蓝牙连接id
                let macAddress;  //蓝牙Mac地址
                if(Platform.OS == 'android'){
                    macAddress = data.id;
                    id = macAddress;
                }else{
                    //ios连接时不需要用到Mac地址，但跨平台识别是否是同一设备时需要Mac地址
                    //如果广播携带有Mac地址，ios可通过广播0x18获取蓝牙Mac地址，
                    macAddress = getMacAddressFromIOS(data);
                    id = data.id;
                }
                cb({...data,macAddress})
            }

        });

    }

    static addNotifyListener(cb){
        // Add event listener
        return bleManagerEmitter.addListener(
            "BleManagerDidUpdateValueForCharacteristic",
            ({ value, peripheral, characteristic, service }) => {
                // Convert bytes array to string

                const data = utf8to16( bytesToString(value) )
                // const data =  bytesToString(value)
                console.log(`Received ${data} for characteristic ${characteristic}，service ${service}`);
                if(this.currentCmd){
                    console.log('-----received cmd-----\n',this.currentCmd);
                    this.cmdResults.push({
                        cmd:this.currentCmd.trim(),
                        result:data.trim()
                    })
                }
                cb(data,this.currentCmd)

                this.currentCmd = null
            }
        );
    }

    static addUpdateStateListener(cb){
        //搜索结束监听
        return bleManagerEmitter.addListener('BleManagerDidUpdateState', (args) => {
            // console.log('BleManagerDidUpdateState:',args);
            cb(args)
        })
    }

    static addDisconnectListener(cb){
        //搜索结束监听
        return bleManagerEmitter.addListener('BleManagerDisconnectPeripheral', (args) => {
            cb(args)
        })
    }

    static addStopScanListener(cb){
        //搜索结束监听
        return bleManagerEmitter.addListener('BleManagerStopScan', (data) => {
            console.log('BleManagerStopScan:',data);
            //搜索结束后，获取搜索到的蓝牙设备列表，如监听了BleManagerDiscoverPeripheral,可省去这个步骤
            cb(data)
        })
    }

    static  getDiscoveredPeripherals(cb){
        BleManager.getDiscoveredPeripherals()
            .then((peripheralsArray) => {
                let list = peripheralsArray.filter(item=>{
                    return item.name && item.name.includes('TABE')
                })
                console.log('Discovered peripherals: ', list);
                cb(list)
            });
    }

    static getConnectedPeripherals(){
        return BleManager.getConnectedPeripherals()
    }

    //android only
    static getBondedPeripherals(){
        return BleManager.getBondedPeripherals()
    }

    //android only
    static enableBluetooth(){
        return BleManager.enableBluetooth()
    }

    static isPeripheralConnected(uuids){
        return BleManager.isPeripheralConnected(uuids,[])
    }

    static scan(){
        return BleManager.scan([],10,true,{
            reportDelay:1,
        })

    }

    static stopScan(){
        return BleManager.stopScan()
    }

    static connect(id,cb){

        let p1 = wake(10)
        let p2 = BleManager.connect(id)
        Promise.race([p1,p2]).then(result=>{
            if(result && result.timeout){
                cb({error:'timeout'})
            }else{
                cb({success:true})
            }
        }).catch(error=>{
            console.log(error)
        }) ;

    }

    static  connectAndPrepare(peripheral){
        return new Promise((fulfill, reject)=> {
            let p1 = wake(10)
            let p2 = BleManager.connect(peripheral)
            Promise.race([p1, p2]).then(result => {
                if (result && result.timeout) {
                    reject({error: 'timeout'})
                } else {
                    BleManager.retrieveServices(peripheral).then(info => {
                        console.log('retrieve:', info);
                        for (let item of info.characteristics) {
                            // console.log('特征item:', item);
                            if(info.name.includes('TABE10')){
                                if (_.isEqual(item.characteristic,BLENotifyServiceUUID) && item.properties.Notify  ) {
                                    console.log('可通知的特征', item);
                                    this.readNotifyCharacteristicUUID = item.characteristic
                                    this.readNotifyServiceUUID = item.service
                                }
                                if (_.isEqual(item.characteristic,BLEWriteServiceUUID) && item.properties.WriteWithoutResponse && item.properties.Write ) {
                                    console.log('可写的特征', item);
                                    this.writeCharacteristicUUID = item.characteristic
                                    this.writeServiceUUID = item.service
                                }
                            }else if(info.name.includes('TABE01')){
                                if (_.isEqual(item.characteristic,'49535343-1e4d-4bd9-ba61-23c647249616') && item.properties.Read && item.properties.Notify  ) {
                                    console.log('可读可通知的特征', item);
                                    this.readNotifyCharacteristicUUID = item.characteristic
                                    this.readNotifyServiceUUID = item.service
                                }
                                if (_.isEqual(item.characteristic,'49535343-8841-43f4-a8d4-ecbe34729bb3') && item.properties.WriteWithoutResponse  ) {
                                    console.log('可写的特征', item);
                                    this.writeCharacteristicUUID = item.characteristic
                                    this.writeServiceUUID = item.service
                                }
                            }

                        }

                        BleManager.startNotification(peripheral,this.readNotifyServiceUUID,this.readNotifyCharacteristicUUID).then(()=>{
                            console.log('notify start');
                            fulfill({success: true})
                            /*
                            if(!isIos()){
                                BleManager.createBond(peripheral).then(() => {
                                    console.log("createBond success or there is already an existing one");
                                }).catch((err) => {
                                    console.log("fail to bond",err);
                                });
                            }
                            */
                        })
                    }).catch(error => {
                        console.log('retrieveServices:',error);
                        reject({error})
                    })

                }
            });

        })

    }


    static startNotification(peripheral, serviceUUID, characteristicUUID){
        return BleManager.startNotification(peripheral, serviceUUID, characteristicUUID)
    }

    static stopNotification(peripheral, serviceUUID, characteristicUUID){
        return BleManager.stopNotification(peripheral, serviceUUID, characteristicUUID)
    }

    static read(peripheral, serviceUUID, characteristicUUID){
        return BleManager.read(peripheral, serviceUUID, characteristicUUID)
    }

    static write(peripheral,command){
        if(!this.writeServiceUUID || !this.writeCharacteristicUUID) return null
        this.currentCmd = command
        const data = stringToBytes( utf16to8( command) )
        return BleManager.write(peripheral, this.writeServiceUUID, this.writeCharacteristicUUID,data)
    }

    static writeWithoutResponse(peripheral,command){
        if(!this.writeServiceUUID || !this.writeCharacteristicUUID) return null
        this.currentCmd = command
        const data = stringToBytes(utf16to8(command))
        return BleManager.writeWithoutResponse(peripheral, this.writeServiceUUID, this.writeCharacteristicUUID,data)
    }

    static checkCmd (){
        return this.currentCmd
    }
    static clearResults (){
        this.cmdResults = []
    }
    static checkResults (){
        return this.cmdResults
    }

    static removePeripheral (id){
        return BleManager.removePeripheral(id)
    }

    static removeBond (id){
        return BleManager.removeBond(id)
    }

    static disconnect(uuid){
        if(this.readNotifyCharacteristicUUID && this.readNotifyServiceUUID){
            return new Promise((fulfill, reject)=> {
                BleManager.stopNotification(uuid,this.readNotifyServiceUUID,this.readNotifyCharacteristicUUID).then(()=>{
                    this.readNotifyServiceUUID = null
                    this.readNotifyCharacteristicUUID = null
                    this.writeCharacteristicUUID = null
                    this.writeServiceUUID = null
                    // console.log('stop notify');
                    BleManager.disconnect(uuid).then(()=>{
                        if(isIos()){
                            fulfill()
                        }else{
                            BleManager.removePeripheral(uuid).then(()=>{
                                console.log('remove peripheral');
                                fulfill()
                            }).catch(err=>{
                                fulfill()
                            })
                        }
                    }).catch(error=>{
                        reject(error)
                    })

                }).catch(error=>{
                    console.log('stop notify err:',error);
                    reject(error)
                })
            })
        }else{
            return BleManager.disconnect(uuid)
        }


    }


    static removeSubscription(name){
        bleManagerEmitter.removeSubscription(name)
    }

    static requestScanPermission(){
        const key = 'android.permission.BLUETOOTH_SCAN'
        const key1 = 'android.permission.ACCESS_COARSE_LOCATION'
        const key2 = 'android.permission.ACCESS_FINE_LOCATION'
        const key3 = 'android.permission.BLUETOOTH_CONNECT'
        const permissions = [key,key1,key2,key3]
        return new Promise((fulfill, reject)=>{
            const promiseList = permissions.map(permission=>{
                return PermissionsAndroid.check(permission)
            })
            Promise.all(promiseList).then(values=>{
                let granted = true
                let requestList = []
                values.forEach((value,index)=>{
                    if(!value){
                        granted = false
                        requestList.push( permissions[index] )
                    }
                })
                if(!granted){
                    PermissionsAndroid.requestMultiple(requestList).then(res=>{
                        console.log('req per:',res);
                        let finalGranted = true
                        Object.values(res).forEach(v=>{
                            if(v === 'denied'){
                                finalGranted = false
                            }
                        })
                        if(finalGranted){
                            fulfill()
                        }else{
                            reject()
                        }
                    }).catch((err)=>{
                        reject()
                        console.log('request err',err);
                    })
                }else{
                    fulfill()
                }
            }).catch((err)=>{
                reject()
                console.log('request err',err);
            })

        })

    }


}

/** ios系统从蓝牙广播信息中获取蓝牙MAC地址 */
function getMacAddressFromIOS(data){
    let macAddressInAdvertising = data.advertising.kCBAdvDataManufacturerMacAddress;
    //为undefined代表此蓝牙广播信息里不包括Mac地址
    if(!macAddressInAdvertising){
        return;
    }
    macAddressInAdvertising = macAddressInAdvertising.replace("<","").replace(">","").replace(" ","");
    if(macAddressInAdvertising != undefined && macAddressInAdvertising != null && macAddressInAdvertising != '') {
        macAddressInAdvertising = swapEndianWithColon(macAddressInAdvertising);
    }
    return macAddressInAdvertising;
}

/**
 * ios从广播中获取的mac地址进行大小端格式互换，并加上冒号:
 * @param str         010000CAEA80
 * @returns string    80:EA:CA:00:00:01
 */
function swapEndianWithColon(str)
{
    let format = '';
    let len = str.length;
    for (let j = 2; j <= len; j = j + 2) {
        format += str.substring(len - j, len - (j - 2));
        if (j != len) {
            format += ":";
        }
    }
    return format.toUpperCase();
}
